home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Games / Xconq 7.0d16 / Xconq 7.0d16 src / mac / macmap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-29  |  56.9 KB  |  2,080 lines  |  [TEXT/KAHL]

  1. /* Copyright (c) 1992, 1993  Stanley T. Shebs. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. /* Map graphics for the Mac interface. */
  6.  
  7. #include "conq.h"
  8. #include "mac.h"
  9.  
  10. extern int daynight;
  11. extern int gridmatchesunseen;
  12.  
  13. #define lighting(x,y,snx,sny)  \
  14.   ((distance(wrapx(x), y, snx, sny) < (3 * world.circumference) / 5) ? 2 : 0)
  15.  
  16. /* True if the given x,y is dark. */
  17.  
  18. #define night_at(x,y) (daynight && lighting((x), (y), sunx, suny) == 0)
  19.  
  20. int tmpdrawlighting;
  21.  
  22. #define hex_overlay(x, y) (tmpdrawlighting ? (night_at(x, y) ? -1 : 0) : 0)
  23.  
  24. #define hex_terrain(x, y, power) (terrain_visible(wrapx(x), y) ? terrain_at(wrapx(x), y) : NONTTYPE)
  25.  
  26. #define hex_style(x, y, power)  \
  27.   (terrain_visible(wrapx(x), y)  ? (power >= 4 ? usepolygons : useblocks) : dontdraw);
  28.  
  29. int seeall = FALSE;
  30.  
  31. int seeallterrain = FALSE;
  32.  
  33. int terrainseen = FALSE;
  34.  
  35. int mayseeall = FALSE;
  36.  
  37. int weseeall = FALSE;
  38.  
  39. /* The width of the left-side control panel. */
  40.  
  41. int conwid = 32;
  42.  
  43. /* The height of the top line. */
  44.  
  45. int tophgt = 16;
  46.  
  47. int mapnum = 1;
  48.  
  49. int nummaps = 0;
  50.  
  51. int lastmaph = -1, lastmapv = -1;
  52.  
  53. /* Handles of the pictures that display all the map control buttons. */
  54.  
  55. PicHandle tlcontrols = nil;
  56. PicHandle blcontrols = nil;
  57.  
  58. char *mouseover = "something";
  59.  
  60. /* This tests whether the given cell might possibly be visible on the given map.
  61.    Not as precise as a real cliprect calculation. */
  62.  
  63. at_all_visible(map, x, y)
  64. Map *map;
  65. int x, y;
  66. {
  67.     int sx, sy;
  68.     
  69.     xform(map, x, y, &sx, &sy);
  70.     sx -= conwid;  sy -= map->toph;
  71.     return ((sx <= map->pxw && sx + map->hw >= 0)
  72.             && (sy <= map->pxh && sy + map->hh >= 0));
  73. }
  74.  
  75. /* Decide whether given location is away from the edge of the map's window. */
  76.  
  77. in_middle(map, x, y)
  78. Map *map;
  79. int x, y;
  80. {
  81.     int sx, sy, insetx1, insety1, insetx2, insety2;
  82.     Point pt;
  83.     Rect tmprect;
  84.     
  85.     xform(map, x, y, &sx, &sy);
  86.     /* Adjust to be the center of the cell, more reasonable if large. */
  87.     sx += map->hw / 2;  sy += map->hh / 2;
  88.     insetx1 = min(map->pxw / 4, 1 * map->hw);  insety1 = min(map->pxh / 4, 1 * map->hch);
  89.     insetx2 = min(map->pxw / 4, 2 * map->hw);  insety2 = min(map->pxh / 4, 2 * map->hch);
  90.     if (sx < conwid + insetx2) return FALSE;
  91.     if (sx > conwid + map->pxw - insetx2) return FALSE;
  92.     if (sy < map->toph + (between(2, y, area.height-3) ? insety2 : insety1)) return FALSE;
  93.     if (sy > map->toph + map->pxh - (between(2, y, area.height-3) ? insety2 : insety1)) return FALSE;
  94.     return TRUE;
  95. }
  96.  
  97. /* Draw an individual detailed hex, as a row of one, on all maps. */
  98.   
  99. update_cell_display(side, x, y, rightnow)
  100. Side *side;
  101. int x, y, rightnow;
  102. {
  103.     int i, sx, sy;
  104.     Unit *unit;
  105.     GrafPtr oldport;
  106.     Map *map;
  107.     RgnHandle tmprgn;
  108.     
  109.     if (active_display(side)) {
  110.         GetPort(&oldport);
  111.         for_all_maps(map) {
  112.             /* If update was only to report temperature, maybe don't bother. */
  113.             if (rightnow == 34 && !map->drawtemperature) continue;
  114.             if (at_all_visible(map, x, y)) {
  115.                 /* Set up the drawing context. */
  116.                 SetPort(map->window);
  117.                 tmprgn = NewRgn();
  118.                 GetClip(tmprgn);
  119.                 /* Clip to the content area of the map's window. */
  120.                 ClipRect(&(map->contentrect));
  121.                 /* Draw the cell. */
  122.                 draw_row(map, x, y, 1, TRUE);
  123.                 /* Draw any selections that are here. */
  124. #if 1 /* (should eventually go away, when basic drawing does this right) */                
  125.                 for (i = 0; i < map->numselections; ++i) {
  126.                     unit = map->selections[i];
  127.                     if (unit && unit->x == x && unit->y == y) {
  128.                         draw_selected_unit(map, unit);
  129.                     }
  130.                 }
  131. #endif
  132.                 SetClip(tmprgn);
  133.                 DisposeRgn(tmprgn);
  134.             }
  135.         }
  136.         SetPort(oldport);
  137.     }
  138. }
  139.  
  140. /* Create a new map window and all its paraphernalia. */
  141.  
  142. Map *
  143. create_map(power)
  144. int power;
  145. {
  146.     int m, sx, sy, i, x, y, h, v;
  147.     Unit *unit;
  148.     Rect tmprect, vscrollrect, hscrollrect;
  149.     Map *map = (Map *) xmalloc(sizeof(Map));
  150.     WindowPtr win;
  151.  
  152.     DGprintf("Creating map, mag power %d\n", power);
  153.     calc_vision();
  154.     set_map_power(map, power);
  155.     /* Pick an appropriate focus of the view. */
  156.     pick_a_focus(dside, &x, &y);
  157.     map->vcx = x;  map->vcy = y;
  158.     /* Set default values for the display controls. */
  159.     map->toph = tophgt;
  160.     map->drawterrain = TRUE;
  161.     map->drawhexpats = TRUE;  /* should get from prefs */
  162.     map->drawunits = TRUE;
  163.     map->drawnames = defaultdrawnames;
  164.     map->drawpeople = FALSE;
  165.     map->drawelevations = FALSE;
  166.     for_all_material_types(m) {
  167.         map->drawmaterials[m] = FALSE;
  168.     }
  169.     map->nummaterialstodraw = 0;
  170.     map->drawlighting = TRUE;
  171.     map->drawtemperature = FALSE;
  172.     map->drawwinds = FALSE;
  173.     map->drawclouds = FALSE;
  174.     map->drawstorms = TRUE;
  175.     map->drawgrid = defaultdrawgrid;
  176.     /* Display AI info by default if there is an AI present. */
  177.     map->drawai = side_has_ai(dside);
  178.     /* Don't indicate other maps by default - too confusing initially. */
  179.     map->drawothermaps = FALSE;
  180.     map->autoselect = defaultautoselect;
  181.     map->moveonclick = defaultmoveonclick;
  182.     map->numselections = 0;
  183.     map->maxselections = max(100, numunits + numunits / 2);
  184.     map->selections = (Unit **) xmalloc(map->maxselections * sizeof(Unit *));
  185.     /* Newest map goes on the front of the list. */
  186.     map->next = maplist;
  187.     maplist = map;
  188.     if (hasColorQD) {
  189.         win = GetNewCWindow(wMap, nil, (WindowPtr) -1L);
  190.     } else {
  191.         win = GetNewWindow(wMap, nil, (WindowPtr) -1L);
  192.     }
  193.     map->window = win;
  194.     stagger_window(win, &lastmaph, &lastmapv);
  195.     ShowWindow(win);
  196.     SetPort(win);
  197.     sprintf(spbuf, "Map %d", mapnum++);
  198.     add_window_menu_item(spbuf, win);
  199.     set_content_rect(map);
  200.     center_on_focus(map);
  201.     set_map_viewport(map);
  202.     /* Make the scrollbars. */
  203.     vscrollrect = map->window->portRect;
  204.     vscrollrect.top -= 1;
  205.     vscrollrect.bottom -= sbarwid - 1;
  206.     vscrollrect.left = vscrollrect.right - sbarwid;
  207.     vscrollrect.right += 1;
  208.     map->vscrollbar =
  209.         NewControl(win, &vscrollrect, "\p", 1,
  210.              map->sy, 0, max(0, map->totsh - map->pxh), scrollBarProc, 0L);
  211.     hscrollrect = win->portRect;
  212.     hscrollrect.top = hscrollrect.bottom - sbarwid;
  213.     hscrollrect.bottom += 1;
  214.     hscrollrect.left += conwid + 1;
  215.     hscrollrect.right -= sbarwid - 1;
  216.     map->hscrollbar =
  217.         NewControl(win, &hscrollrect, "\p", 1,
  218.              map->sx, 0, max(0, map->totsw - map->pxw), scrollBarProc, 0L);
  219.     set_map_scrollbars(map);
  220.     ++nummaps;
  221.     return map;
  222. }
  223.  
  224. calc_vision()
  225. {
  226.     /* (should be in kernel) */
  227.     seeall = g_see_all();
  228.     terrainseen = g_terrain_seen();
  229.     seeallterrain = all_terrain_visible();   /* (should tweak when vars change) */
  230. }
  231.  
  232. /* Compute the content part of the map window. */
  233.  
  234. set_content_rect(map)
  235. Map *map;
  236. {
  237.     map->contentrect = map->window->portRect;
  238.     map->contentrect.left += conwid;  map->contentrect.top += map->toph;
  239.     map->contentrect.right -= sbarwid;  map->contentrect.bottom -= sbarwid;
  240.     map->pxw = (map->contentrect.right - map->contentrect.left);
  241.     map->pxh = (map->contentrect.bottom - map->contentrect.top);
  242. }
  243.  
  244. /* Set vcx/vcy to point to the center of the displayed map. */
  245.  
  246. focus_on_center(map)
  247. Map *map;
  248. {
  249.     map->vcy = (map->totsh - (map->sy + map->pxh / 2)) / map->hch;
  250.     map->vcx = map->sx / map->hw - (map->vcy / 2) + map->vh / 2;
  251.     /* Restrict the focus to be *inside* the area. */
  252.     map->vcy = max(1, min(map->vcy, area.height - 2));
  253.     if (area.xwrap) {
  254.         map->vcx = wrapx(map->vcx);
  255.     } else {
  256.         map->vcx = max(1, min(map->vcx, area.width - 2));
  257.         if (map->vcx + map->vcy < area.height / 2 + 1)
  258.             map->vcx = area.height / 2 + 1;
  259.         if (map->vcx + map->vcy > area.width + area.height / 2 - 1)
  260.             map->vcx = area.width + area.height / 2 - 1;
  261.     }
  262. }
  263.  
  264. /* Put vcx,vcy in the middle of the map. */
  265.  
  266. center_on_focus(map)
  267. Map *map;
  268. {
  269.     /* Scale, add hex offset adjustment, translate to get left edge. */
  270.     map->sx = map->vcx * map->hw + (map->vcy * map->hw) / 2 - map->pxw / 2 + map->hw / 2;
  271.     /* Scale, translate to top edge, flip. */
  272.     map->sy = map->totsh - (map->vcy * map->hch + map->pxh / 2 + map->hh / 2);
  273.     /* Weird vcx,vcy might make sx,sy nonsensical, so clip to rational limits. */
  274.     map->sx = max(0, min(map->sx, map->totsw - map->pxw));
  275.     map->sy = max(0, min(map->sy, map->totsh - map->pxh));
  276.     DGprintf("Mapwin at %d,%d, focused at %d,%d\n", map->sx, map->sy, map->vcx, map->vcy);
  277. }
  278.  
  279. /* Adjust the appearance and thumb of the scroll bars to reflect the map.  This is
  280.    needed whenever the map is scrolled under program control, such as when magnifying
  281.    or scrolling to a specified location. */
  282.  
  283. set_map_scrollbars(map)
  284. Map *map;
  285. {
  286.     int hilite;
  287.  
  288.     if (map->pxw < (map->totsw - hexagon_adjust(map))) {
  289.         /* Set horiz min so that leftmost hex in area is at edge of window. */
  290.         SetCtlMin(map->hscrollbar, hexagon_adjust(map));
  291.         /* Wrapped-around areas need extra room to look at cells on the seam. */
  292.         SetCtlMax(map->hscrollbar, map->totsw - (area.xwrap ? 0 : map->pxw));
  293.         SetCtlValue(map->hscrollbar, map->sx);
  294.         hilite = TRUE;
  295.     } else {
  296.         /* Force sx to a default value and disable the scrollbar. */
  297.         map->sx = hexagon_adjust(map);
  298.         hilite = FALSE;
  299.     }
  300.     /* Adjust the hiliting of the scrollbar, but only if the window is in front,
  301.        otherwise the scrollbar should remain unhilited. */
  302.     if (map->window == FrontWindow()) {
  303.         HiliteControl(map->hscrollbar, (hilite ? 0 : 255));
  304.     }
  305.     DGprintf("Hscroll (%shilite) is %d -- %d -- %d\n",
  306.              (hilite ? "" : "no "), GetCtlMin(map->hscrollbar),
  307.              GetCtlValue(map->hscrollbar), GetCtlMax(map->hscrollbar));
  308.     if (map->pxh < map->totsh) {
  309.         /* Vertical scrollbar min is always zero. */
  310.         SetCtlMax(map->vscrollbar, map->totsh - map->pxh);
  311.         /* Constrain the scaled y position of the map.  This keeps the computed
  312.            y from exceeding what the scroll bar will allow (which happens because
  313.            the sy calcs don't take scroll bar limits into account, should be fixed). */
  314.         if (map->sy > map->totsh - map->pxh) map->sy = map->totsh - map->pxh;
  315.         SetCtlValue(map->vscrollbar, map->sy);
  316.         hilite = TRUE;
  317.     } else {
  318.         /* Force sy to the top of the map and disable the scrollbar. */
  319.         map->sy = 0;
  320.         hilite = FALSE;
  321.     }
  322.     if (map->window == FrontWindow()) {
  323.         HiliteControl(map->vscrollbar, (hilite ? 0 : 255));
  324.     }
  325.     DGprintf("Vscroll (%shilite) is %d -- %d -- %d\n",
  326.              (hilite ? "" : "no "), GetCtlMin(map->vscrollbar),
  327.              GetCtlValue(map->vscrollbar), GetCtlMax(map->vscrollbar));
  328. }
  329.  
  330. /* Given a magnification power, look up and/or calculate the sizes of everything,
  331.    in pixels. */
  332.  
  333. set_map_power(map, power)
  334. Map *map;
  335. int power;
  336. {
  337.     map->power = power;
  338.     map->mag = mags[power]; /* is this used?? */
  339.     map->hw = hws[power];  map->hh = hhs[power];  map->hch = hcs[power];
  340.     map->uw = uws[power];  map->uh = uhs[power];
  341.     /* Calculate and cache the width in pixels of the whole area, adding an
  342.        an adjustment to account for the "bulge" of hexagon-shaped areas. */
  343.     map->totsw = area.width * map->hw + hexagon_adjust(map);
  344.     /* Total scaled height is based on center-to-center height, plus an adjustment
  345.        to include the bottom parts of the bottom row. */
  346.     map->totsh = area.height * map->hch + (map->hh - map->hch);
  347.     if (power >= 4 && cellrgns[power] == nil) make_cell_clip(power);
  348.     DGprintf("Power is now %d, total scaled area is %d x %d\n",
  349.              map->power, map->totsw, map->totsh);
  350. }
  351.  
  352. make_cell_clip(power)
  353. int power;
  354. {
  355.     int hw = hws[power], hh = hhs[power], delt = (hhs[power] - hcs[power]);
  356.     PolyHandle poly;
  357.     RgnHandle tmprgn;
  358.  
  359.     poly = OpenPoly();
  360.     MoveTo(hw / 2, 0);
  361.     LineTo(hw, delt);
  362.     LineTo(hw, hh - delt);
  363.     LineTo(hw / 2, hh);
  364.     LineTo(0, hh - delt);
  365.     LineTo(0, delt);
  366.     LineTo(hw / 2, 0);
  367.     ClosePoly();
  368.     cellrgns[power] = NewRgn();
  369.     OpenRgn();
  370.     FramePoly(poly);
  371.     CloseRgn(cellrgns[power]);
  372.     gridcellrgns[power] = NewRgn();
  373.     CopyRgn(cellrgns[power], gridcellrgns[power]);
  374.     tmprgn = NewRgn();
  375.     SetRectRgn(tmprgn, hw - 1, 0, hw + 1, hh); 
  376.     DiffRgn(gridcellrgns[power], tmprgn, gridcellrgns[power]);
  377.     tmprgn = NewRgn();
  378.     CopyRgn(cellrgns[power], tmprgn);
  379.     OffsetRgn(tmprgn, 0, -1);
  380.     SectRgn(gridcellrgns[power], tmprgn, gridcellrgns[power]);
  381. }
  382.  
  383. /* Given a map with already-set pixel size and position, compute
  384.    reasonable values for its size and position in area coordinates.
  385.    This is really just caching, can be run at any time. */
  386.  
  387. set_map_viewport(map)
  388. Map *map;
  389. {
  390.     /* Compute the size of the viewport.  Make sure it will extend past the edges
  391.        of the window, so that partial hexes around the edges will be filled in. */
  392.     map->vw = min(area.width, map->pxw / map->hw + 2);
  393.     map->vh = min(area.height, map->pxh / map->hch + 2);
  394.     /* Compute the bottom visible row. */
  395.     map->vy = ((map->totsh - map->sy) / map->hch) - map->vh;
  396.     /* Now adjust the bottom row so it doesn't go outside the area. */
  397.     if (map->vy < 0) map->vy = 0;
  398.     if (map->vy > area.height - map->vh) map->vy = area.height - map->vh;
  399.     /* Compute the leftmost "column". */
  400.     map->vx = map->sx / map->hw - map->vy / 2 - 1;
  401.     DGprintf("Set %dx%d viewport at %d,%d\n", map->vw, map->vh, map->vx, map->vy);
  402. }
  403.  
  404. /* Given a window, find the map that it belongs to. */
  405.  
  406. Map *
  407. map_from_window(window)
  408. WindowPtr window;
  409. {
  410.     Map *map;
  411.     
  412.     if (dside == NULL) return NULL;
  413.     for_all_maps(map) {
  414.         if (map->window == window) return map;
  415.     }
  416.     return NULL;
  417. }
  418.  
  419. /* Set the size of the map window and position its scrollbars correctly. */
  420.  
  421. grow_map(map, w, h)
  422. Map *map;
  423. int w, h;
  424. {
  425. /*    EraseRect(&map->window->portRect); */
  426.     SizeWindow(map->window, w, h, 1);
  427.     adjust_map_decor(map);
  428.     draw_related_maps(map);
  429. }
  430.  
  431. /* Map zooming actually does "rightsizing" if possible. */
  432.  
  433. zoom_map(map, part)
  434. Map *map;
  435. short part;
  436. {
  437.     WindowPtr mapwin = map->window;
  438.  
  439.     if (part == inZoomOut) {
  440.         set_standard_state(mapwin,
  441.                            area.width * map->hw + conwid + sbarwid + 1,
  442.                            area.height * map->hch + (map->hh - map->hch) + sbarwid + map->toph + 1);
  443.     }
  444.     EraseRect(&mapwin->portRect);
  445.     ZoomWindow(mapwin, part, true);
  446.     adjust_map_decor(map);
  447. }
  448.  
  449. GDHandle
  450. best_zoom_screen(rectptr)
  451. Rect *rectptr;
  452. {
  453.     int greatestarea = 0, sectarea;
  454.     Rect srect;
  455.     GDHandle screen = GetDeviceList(), bestscreen = nil;
  456.  
  457.     while (screen != nil) {
  458.         if (TestDeviceAttribute(screen, screenDevice)
  459.             && TestDeviceAttribute(screen, screenActive)) {
  460.             SectRect(rectptr, &((*screen)->gdRect), &srect);
  461.             sectarea = (srect.right - srect.left) * (srect.bottom - srect.top);
  462.             if (sectarea > greatestarea) {
  463.                 greatestarea = sectarea;
  464.                 bestscreen = screen;
  465.             }
  466.         }
  467.         screen = GetNextDevice(screen);
  468.     }
  469.     return bestscreen;
  470. }
  471.  
  472. set_standard_state(win, fullw, fullh)
  473. WindowPtr win;
  474. int fullw, fullh;
  475. {
  476.     int screenw, screenh, wintitlehgt, mbaradj = 0;
  477.     Rect winrect, gdrect, zoomrect;
  478.     GDHandle bestscreen;
  479.  
  480.     if (!hasColorQD) {
  481.         zoomrect = screenBits.bounds;
  482.         InsetRect(&zoomrect, 4, 4);
  483.     } else {
  484.         winrect = win->portRect;
  485.         LocalToGlobal((Point *) &(winrect.top));
  486.         LocalToGlobal((Point *) &(winrect.bottom));
  487.         wintitlehgt = winrect.top - 1 - (*(((WindowPeek) win)->strucRgn))->rgnBBox.top;
  488.         /* Get the best screen to zoom on. */
  489.         bestscreen = best_zoom_screen(&winrect);
  490.         gdrect = (*bestscreen)->gdRect;
  491.         /* Adjust to the actual subarea that we can use. */
  492.         if (bestscreen == GetMainDevice()) {
  493.             gdrect.top += GetMBarHeight();
  494.         }
  495.         InsetRect(&gdrect, 3, 3);
  496.         gdrect.top += wintitlehgt;
  497.         screenw = gdrect.right - gdrect.left;  screenh = gdrect.bottom - gdrect.top;
  498.         if (winrect.left + fullw <= gdrect.right
  499.             && winrect.top + fullh <= gdrect.bottom) {
  500.             SetRect(&zoomrect, winrect.left, winrect.top, winrect.left + fullw, winrect.top + fullh);
  501.         } else if (fullw <= screenw || fullh <= screenh) {
  502.             SetRect(&zoomrect, gdrect.left, gdrect.top, gdrect.left + fullw, gdrect.top + fullh);
  503.             if (fullw > screenw) zoomrect.right = gdrect.right;
  504.             if (fullh > screenh) zoomrect.bottom = gdrect.bottom;
  505.         } else {
  506.             zoomrect = gdrect;
  507.         }
  508.     }
  509.     ((WStateDataPtr) *(((WindowPeek) win)->dataHandle))->stdState = zoomrect;
  510. }
  511.  
  512. /* Move and size the controls to be correct for the map. */
  513.  
  514. adjust_map_decor(map)
  515. Map *map;
  516. {                
  517.     int w, h;
  518.  
  519.     w = map->window->portRect.right - map->window->portRect.left;
  520.     h = map->window->portRect.bottom - map->window->portRect.top;
  521. /*    HideControl(map->hscrollbar); */
  522.     MoveControl(map->hscrollbar, conwid, h - sbarwid);
  523.     SizeControl(map->hscrollbar, w - conwid - sbarwid + 1, sbarwid + 1);
  524. /*    HideControl(map->vscrollbar); */
  525.     MoveControl(map->vscrollbar, w - sbarwid, -1);
  526.     SizeControl(map->vscrollbar, sbarwid + 1, h - sbarwid + 1 + 1);
  527.     set_content_rect(map);
  528.     InvalRect(&map->window->portRect);
  529. }
  530.  
  531. /* Given a map and a cell, compute the pixel coords of the cell's UL corner.
  532.    This is the core routine that relates cells and pixels. */
  533.  
  534. void
  535. xform(map, x, y, sxp, syp)
  536. Map *map;
  537. int x, y, *sxp, *syp;
  538. {
  539.     if (in_area(x, y)) {
  540.         *sxp = x * map->hw + (y * map->hw) / 2 - map->sx;
  541.         *syp = (map->totsh - (map->hh + y * map->hch)) - map->sy;
  542.     } else {
  543.         /* Always complain about this, indicates bugs. */
  544.         run_warning("attempting to xform %d,%d", x, y);
  545.         /* But it's not fatal, so return a semi-plausible position. */
  546.         *sxp = *syp = 1;
  547.     }
  548.     /* Adjust for the control panel and topline displays. */
  549.     *sxp += conwid;  *syp += map->toph;
  550. }
  551.  
  552. void
  553. xform_unit(map, unit, sxp, syp, swp, shp)
  554. Map *map;
  555. Unit *unit;
  556. int *sxp, *syp, *swp, *shp;
  557. {
  558.     int num = 0, n = -1, sq, sx, sy, sx1, sy1, sw1, sh1, x = unit->x, y = unit->y;
  559.     Unit *unit2;
  560.  
  561.     if (unit->transport == NULL) {
  562.         xform(map, x, y, &sx, &sy);
  563.         /* Adjust to the unit box within the cell. */
  564.         sx += (map->hw - map->uw) / 2;  sy += (map->hh - map->uh) / 2;
  565.         /* Figure out our position in this cell's stack. */
  566.         for_all_stack(x, y, unit2) {
  567.             if (unit == unit2) n = num;
  568.             ++num;
  569.         }
  570.         if (n < 0) run_error("xform_unit weirdness");
  571.         if (num <= 1) {
  572.             sq = 1;
  573.         } else if (num <= 4) {
  574.             sq = 2;
  575.         } else if (num <= 16) {
  576.             sq = 4;
  577.         } else if (num <= 256) {
  578.             sq = 8;
  579.         } else {
  580.             /* This is room for 65,536 units in a stack. */
  581.             sq = 16;
  582.         }
  583.         *swp = map->uw / sq;  *shp = map->uh / sq;
  584.         *sxp = sx + *swp * (n / sq);  *syp = sy + *shp * (n % sq);
  585.     } else {
  586.         /* Go up the transport chain to get the bounds for this unit. */
  587.         xform_unit(map, unit->transport, &sx1, &sy1, &sw1, &sh1);
  588.         xform_occupant(map, unit->transport, unit, sx1, sy1, sw1, sh1, sxp, syp, swp, shp);
  589.     }
  590. }
  591.  
  592. void
  593. xform_unit_self(map, unit, sxp, syp, swp, shp)
  594. Map *map;
  595. Unit *unit;
  596. int *sxp, *syp, *swp, *shp;
  597. {
  598.     int num, n, sq, sx1, sy1, sw1, sh1, x = unit->x, y = unit->y;
  599.     Unit *unit2;
  600.  
  601.     if (unit->transport == NULL) {
  602.         if (unit->occupant == NULL) {
  603.             xform_unit(map, unit, sxp, syp, swp, shp);
  604.         } else {
  605.             xform_unit(map, unit, &sx1, &sy1, &sw1, &sh1);
  606.             xform_occupant(map, unit, unit, sx1, sy1, sw1, sh1, sxp, syp, swp, shp);
  607.         }
  608.     } else {
  609.         xform_unit(map, unit->transport, &sx1, &sy1, &sw1, &sh1);
  610.         xform_occupant(map, unit->transport, unit, sx1, sy1, sw1, sh1, sxp, syp, swp, shp);
  611.     }
  612. }
  613.  
  614. void
  615. xform_occupant(map, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp)
  616. Map *map;
  617. Unit *transport, *unit;
  618. int sx, sy, sw, sh, *sxp, *syp, *swp, *shp;
  619. {
  620.     int num = 0, n = -1, nmx, nmy;
  621.     Unit *unit2;
  622.  
  623.     /* Figure out the position of this unit amongst all the occupants. */
  624.     for_all_occupants(transport, unit2) {
  625.         if (unit2 == unit) n = num;
  626.         ++num;
  627.     }
  628.     if (unit == transport) {
  629.         if (num > 0) {
  630.             /* Transport image shrinks by half in each dimension. */
  631.             *swp = sw / 2;  *shp = sh / 2;
  632.         }
  633.         /* Transport is always in the UL corner. */
  634.         *sxp = sx;  *syp = sy;
  635.     } else {
  636.         if (n < 0) run_error("xform_occupant weirdness");
  637.         /* Compute how the half-box will be subdivided.  Only use powers of two,
  638.            so image scaling works better. */
  639.         if (num <= 2) {
  640.             nmx = 2;
  641.         } else if (num <= 8) {
  642.             nmx = 4;
  643.         } else if (num <= 128) {
  644.             nmx = 8;
  645.         } else {
  646.             /* This is room for 32,768 units in a stack. */
  647.             nmy = 16;
  648.         }
  649.         nmy = nmx / 2;
  650.         *swp = sw / nmx;  *shp = (sh / 2) / nmy;
  651.         *sxp = sx + *swp * (n / nmy);  *syp = sy + sh / 2 + *shp * (n % nmy);
  652.     }
  653. }
  654.  
  655. /* Un-transform screen coordinates (as supplied by mouse perhaps) into
  656.    map coordinates.  This routine doesn't yet
  657.    pay attention to hexagon boundaries, using only box-shaped areas,
  658.    but in practice (for small hexes only) this seems to be OK. */
  659.  
  660. nearest_cell(map, sx, sy, xp, yp)
  661. Map *map;
  662. int sx, sy, *xp, *yp;
  663. {
  664.     /* Subtract offset due to map decor. */
  665.     sx -= conwid;
  666.     sy -= map->toph;
  667.     /* Flip the raw y and then scale to hex coords. */
  668.     *yp = (map->totsh - (map->sy + sy)) / map->hch;
  669.     /* Scale adjusted x to hex coord. */
  670.     *xp = (sx + map->sx - (*yp * map->hw) / 2) / map->hw;
  671.     /* If the magnification of the map is large enough that the top and bottom
  672.        edges of a hex are visibly sloping, then we have to take those edges
  673.        int account, and accurately. */
  674.     if ((map->hh - map->hch) / 2 > 1) {
  675.         /* (should adjust according to hex boundaries correctly here) */
  676.     }
  677.     /* Wrap coords as usual. */
  678.     if (area.xwrap) *xp = wrapx(*xp);
  679.     DGprintf("Pixel %d,%d -> hex %d,%d\n", sx, sy, *xp, *yp);
  680.     return (in_area(*xp, *yp));
  681. }
  682.  
  683. /* Find the closest direction of the closest boundary. */
  684.  
  685. nearest_boundary(map, sx, sy, xp, yp, dirp)
  686. Map *map;
  687. int sx, sy, *xp, *yp, *dirp;
  688. {
  689.     int sx2, sy2, ydelta, hexslope;
  690.  
  691.     /* Get the nearest cell... */
  692.     nearest_cell(map, sx, sy, xp, yp);
  693.     /* ... and xform it back to get the pixel coords. */ 
  694.     xform(map, *xp, *yp, &sx2, &sy2);
  695.     ydelta = sy - sy2;
  696.     hexslope = (map->hh - map->hch) / 2;
  697.     if (sx - sx2 > map->hw / 2) {
  698.         *dirp = ((ydelta < hexslope) ? NORTHEAST : (ydelta > map->hch ? SOUTHEAST : EAST));
  699.     } else {
  700.         *dirp = ((ydelta < hexslope) ? NORTHWEST : (ydelta > map->hch ? SOUTHWEST : WEST));
  701.     }
  702.     DGprintf("Pixel %d,%d -> hex %d,%d dir %d\n", sx, sy, *xp, *yp, *dirp); 
  703. }
  704.  
  705. Unit *
  706. find_unit_or_occ(map, unit, usx, usy, usw, ush, sx, sy)
  707. Map *map;
  708. Unit *unit;
  709. int usx, usy, usw, ush, sx, sy;
  710. {
  711.     int usx1, usy1, usw1, ush1;
  712.     Unit *occ, *rslt;
  713.  
  714.     /* See if the point might be over an occupant. */
  715.     if (unit->occupant != NULL) {
  716.         for_all_occupants(unit, occ) {
  717.             xform_unit(map, occ, &usx1, &usy1, &usw1, &ush1);
  718.             if ((rslt = find_unit_or_occ(map, occ, usx1, usy1, usw1, ush1, sx, sy))) {
  719.                 return rslt;
  720.             }
  721.         }
  722.     }
  723.     /* Otherwise see if it could be the unit itself.  This has the effect of
  724.        "giving" the transport everything in its box that is not in an occ. */
  725.     xform_unit(map, unit, &usx1, &usy1, &usw1, &ush1);
  726.     if (between(usx1, sx, usx1 + usw1) && between(usy1, sy, usy1 + ush1)) {
  727.         return unit;
  728.     }
  729.     return NULL;
  730. }
  731.  
  732. Unit *
  733. find_unit_at(map, x, y, sx, sy)
  734. Map *map;
  735. int x, y, sx, sy;
  736. {
  737.     int usx, usy, usw, ush;
  738.     Unit *unit, *rslt;
  739.     
  740.     for_all_stack(x, y, unit) {
  741.         xform_unit(map, unit, &usx, &usy, &usw, &ush);
  742.         if ((rslt = find_unit_or_occ(map, unit, usx, usy, usw, ush, sx, sy))) {
  743.             return rslt;
  744.         }
  745.     }
  746.     return NULL;
  747. }
  748.  
  749. nearest_unit(map, sx, sy, unitp)
  750. Map *map;
  751. int sx, sy;
  752. Unit **unitp;
  753. {
  754.     int x, y;
  755.  
  756.     if (!nearest_cell(map, sx, sy, &x, &y)) {
  757.         *unitp = NULL;
  758.     } else if (map->power > 4) {
  759.         *unitp = find_unit_at(map, x, y, sx, sy);
  760.     } else {
  761.         *unitp = unit_at(x, y);
  762.     }
  763.     DGprintf("Pixel %d,%d -> unit %s\n", sx, sy, unit_desig(*unitp)); 
  764. }
  765.  
  766. erase_map(map)
  767. Map *map;
  768. {
  769.     BackPat(QD(gray));
  770.     EraseRect(&(map->window->portRect));
  771. }
  772.  
  773. /* Display a map and all of its paraphernalia. */
  774.  
  775. draw_map(map)
  776. Map *map;
  777. {
  778.     Rect tmprect;
  779.     WindowPtr mapwin = map->window;
  780.     RgnHandle tmprgn;
  781.  
  782.     /* Draw control panel and topline before clipping to inner part of window. */
  783.     draw_control_panel(map);
  784.     if (map->toph > 0) draw_top_line(map);
  785.     tmprgn = NewRgn();
  786.     GetClip(tmprgn);
  787.     ClipRect(&(map->contentrect));
  788.     /* Calculate shapes and sizes. */
  789.     /* If part of the window is entirely outside the world, we draw its shape on
  790.        top of gray, otherwise window starts out all white. */
  791.     if (area.width * map->hw < 32000) {
  792.         switch (bggray) {
  793.             case blackgray:
  794.                 FillRect(&(map->contentrect), QD(black));  break;
  795.             case darkgray:
  796.                 FillRect(&(map->contentrect), QD(dkGray));  break;
  797.             case mediumgray:
  798.                 FillRect(&(map->contentrect), QD(gray));  break;
  799.             case lightgray:
  800.                 FillRect(&(map->contentrect), QD(ltGray));  break;
  801.             case whitegray:
  802.                 FillRect(&(map->contentrect), QD(white));  break;
  803.         }
  804.     } else {
  805.         if (hasColorQD) {
  806.             RGBForeColor((gridmatchesunseen ? &gridcolor : &unseencolor));
  807.             PaintRect(&(map->contentrect));
  808.             RGBForeColor(&blackcolor);
  809.         } else {
  810.             switch ((gridmatchesunseen ? gridgray : unseengray)) {
  811.                 case blackgray:
  812.                     FillRect(&(map->contentrect), QD(black));  break;
  813.                 case darkgray:
  814.                     FillRect(&(map->contentrect), QD(dkGray));  break;
  815.                 case mediumgray:
  816.                     FillRect(&(map->contentrect), QD(gray));  break;
  817.                 case lightgray:
  818.                     FillRect(&(map->contentrect), QD(ltGray));  break;
  819.                 case whitegray:
  820.                     FillRect(&(map->contentrect), QD(white));  break;
  821.             }
  822.         }
  823.     }
  824.     set_map_scrollbars(map);
  825.     set_map_viewport(map);
  826.     draw_area_background(map);
  827.     draw_map_content(map);
  828.     if (map->drawothermaps) draw_other_maps(map);
  829.     draw_selections(map);
  830. #ifdef DEBUGGING
  831.     /* Indicate where the focus is. */
  832.     if (DebugG) {
  833.         int sx, sy;
  834.  
  835.         xform(map, map->vcx, map->vcy, &sx, &sy);
  836.         SetRect(&tmprect, sx, sy, sx + map->hw, sy + map->hh);
  837.         InsetRect(&tmprect, -4, -4);
  838.         InvertOval(&tmprect);
  839.         InsetRect(&tmprect, 2, 2);
  840.         InvertOval(&tmprect);
  841.     }
  842. #endif /* DEBUGGING */
  843.     SetClip(tmprgn);
  844.     DisposeRgn(tmprgn);
  845. }
  846.  
  847. /* Draw the actual map data. */
  848.  
  849. draw_map_content(map)
  850. Map *map;
  851. {
  852.     int y1, y2, y, x1, x2, xx1, yy1, xx2, yy2;
  853.     int halfheight = area.height / 2;
  854.     int limitleft = FALSE, limitrite = FALSE;
  855.     Rect bbox = (*(map->window->visRgn))->rgnBBox;
  856.     Rect tmprect = map->contentrect;
  857.     extern int endofgame;
  858.  
  859.     ClipRect(&(map->contentrect));
  860.     if (DebugG) {
  861.         FillRgn(map->window->visRgn, QD(white));
  862.     }
  863.     /* Compute top and bottom rows to be displayed. */
  864.     y1 = min(map->vy + map->vh, area.height - 1);
  865.     y2 = map->vy;
  866.     /* Find the top and bottom rows that are in the visRgn. */
  867.     if (nearest_cell(map, bbox.left, bbox.top, &xx1, &yy1))
  868.       limitleft = TRUE;
  869.     if (nearest_cell(map, bbox.right + map->hw, bbox.bottom + map->hh, &xx2, &yy2))
  870.       limitrite = TRUE;
  871.     DGprintf("Map rows are %d - %d, update area rows are %d - %d\n", y2, y1, yy2, yy1);
  872.     if (between(y2, yy1, y1)) y1 = yy1; 
  873.     if (between(y2, yy2, y1)) y2 = yy2;
  874.     for (y = y1; y >= y2; --y) {
  875.         /* Adjust the right and left bounds to fill the viewport as
  876.            much as possible, without going too far (the drawing code
  877.            will clip, but clipped drawing is still expensive). */
  878.         /* could test by drawing viewport rect as lines... */
  879.         x1 = map->vx - (y - map->vy) / 2;
  880.         x2 = x1 + map->vw + 2 /* bleah, shouldn't be necessary */;
  881.         /* If the area doesn't wrap, then we might have to stop
  882.            drawing before we reach the edge of the viewport. */
  883.         if (!area.xwrap) {
  884.             /* Truncate x's to stay within the area. */
  885.             x1 = max(0, min(x1, area.width-1));
  886.             x2 = max(0, min(x2, area.width));
  887.             /* If this row is entirely in the NE corner, don't draw anything. */
  888.             if (x1 + y > area.width + halfheight)
  889.               continue;
  890.             /* If this row is entirely in the SW corner, don't draw anything. */
  891.             if (x2 + y < halfheight)
  892.               continue;
  893.             /* If the row ends up in the NE corner, shorten it. */
  894.             if (x2 + y > area.width + halfheight)
  895.               x2 = area.width + halfheight - y;
  896.             /* If the row starts out in the SW corner, shorten it. */
  897.             if (x1 + y < halfheight)
  898.               x1 = halfheight - y;
  899.         }
  900.         /* Clip the ends of the row to the visRgn. */
  901.         if (limitleft && between(x1, xx1, x2)) x1 = xx1; 
  902.         if (limitrite && between(x1, xx2, x2)) x2 = xx2;
  903.         draw_row(map, x1, y, x2 - x1, FALSE);
  904.     }
  905.     /* If the game is over, draw a large X over the map. */
  906.     if (endofgame) {
  907.         PenSize(2, 2);
  908.         PenPat(QD(dkGray));
  909.         MoveTo(tmprect.left,  tmprect.top);  LineTo(tmprect.right, tmprect.bottom);
  910.         MoveTo(tmprect.right, tmprect.top);  LineTo(tmprect.left,  tmprect.bottom);
  911.         PenNormal();
  912.     }
  913. }
  914.  
  915. /* This draws a hexagon or rectangle that covers the total area of the area, whether
  916.    seen or not. */
  917.  
  918. draw_area_background(map)
  919. Map *map;
  920. {
  921.     int sx, sy, x, y;
  922.     int llx, lly, lrx, lry, rx, ry, urx, ury, ulx, uly, lx, ly;
  923.     PolyHandle poly;
  924.     Rect arearect;
  925.  
  926.     if (area.width * map->hw < 32000) {
  927.         if (area.xwrap) {
  928.             xform(map, 0, 0, &sx, &sy);
  929.             arearect.left = 0;  arearect.top = 0;
  930.             arearect.right = area.width * map->hw;  arearect.bottom = sy;
  931.             if (hasColorQD) {
  932.                 RGBForeColor(&gridcolor);
  933.                 PaintRect(&arearect);
  934.                 RGBForeColor(&blackcolor);
  935.             } else {
  936.                 switch (gridgray) {
  937.                     case blackgray:
  938.                         FillRect(&arearect, QD(black));   break;
  939.                     case darkgray:
  940.                         FillRect(&arearect, QD(dkGray));  break;
  941.                     case mediumgray:
  942.                         FillRect(&arearect, QD(gray));    break;
  943.                     case lightgray:
  944.                         FillRect(&arearect, QD(ltGray));  break;
  945.                     case whitegray:
  946.                         FillRect(&arearect, QD(white));   break;
  947.                 }
  948.             }
  949.         } else {
  950.             /* (should make once and save?) */
  951.             poly = OpenPoly();        
  952.             xform(map, 0 + area.height/2, 0, &llx, &lly);
  953.             MoveTo(llx, lly);
  954.             xform(map, area.width-1, 0, &lrx, &lry);
  955.              LineTo(lrx, lry);
  956.             xform(map, area.width-1, area.height/2, &rx, &ry);
  957.             LineTo(rx, ry);
  958.              xform(map, area.width-1 - area.height/2, area.height-1, &urx, &ury);
  959.             LineTo(urx, ury);
  960.              xform(map, 0, area.height-1, &ulx, &uly);
  961.             LineTo(ulx, uly);
  962.              xform(map, 0, area.height/2, &lx, &ly);
  963.             LineTo(lx, ly);
  964.             LineTo(llx, lly);
  965.             ClosePoly();
  966.             OffsetPoly(poly, map->hw/2, map->hh/2);
  967.             if (hasColorQD) {
  968.                 RGBForeColor(&gridcolor);
  969.                 PaintPoly(poly);
  970.                 RGBForeColor(&blackcolor);
  971.             } else {
  972.                 switch (gridgray) {
  973.                     case blackgray:
  974.                         FillPoly(poly, QD(black));   break;
  975.                     case darkgray:
  976.                         FillPoly(poly, QD(dkGray));  break;
  977.                     case mediumgray:
  978.                         FillPoly(poly, QD(gray));    break;
  979.                     case lightgray:
  980.                         FillPoly(poly, QD(ltGray));  break;
  981.                     case whitegray:
  982.                         FillPoly(poly, QD(white));   break;
  983.                 }
  984.             }
  985.         }
  986. #if 0
  987.         /* This doesn't look very good in practice. */
  988.         for (x = 0; x < area.width; ++x) {
  989.             xform(map, x, 0, &sx, &sy);
  990.             draw_border_line(sx, sy, SW, map->power, -1);
  991.             draw_border_line(sx, sy, SE, map->power, -2);
  992.         }
  993.         PenNormal();
  994. #endif
  995.     }
  996. }
  997.  
  998. /* Draw the map control panel as a pair of PICTs. */
  999.  
  1000. draw_control_panel(map)
  1001. Map *map;
  1002. {
  1003.     int winhgt, baseh, basev;
  1004.     Rect tmprect;
  1005.  
  1006.     winhgt = (map->window->portRect).bottom - (map->window->portRect).top;
  1007.     SetRect(&tmprect, 0, 0, conwid, winhgt);
  1008.     FillRect(&tmprect, QD(white));
  1009.     MoveTo(conwid - 1, 0);  Line(0, winhgt);
  1010.     if (tlcontrols == nil) {
  1011.         tlcontrols = (PicHandle) GetResource('PICT', pMapControlsTL);
  1012.     }
  1013.     if (tlcontrols != nil) {
  1014.         SetRect(&tmprect, 0, 0,
  1015.                 picture_width(tlcontrols), picture_height(tlcontrols));
  1016.         DrawPicture(tlcontrols, &tmprect);
  1017.     }
  1018.     if (blcontrols == nil) {
  1019.         blcontrols = (PicHandle) GetResource('PICT', pMapControlsBL);
  1020.     }
  1021.     if (blcontrols != nil) {
  1022.         SetRect(&tmprect, 0, winhgt - picture_height(blcontrols),
  1023.                 picture_width(blcontrols), winhgt);
  1024.         DrawPicture(blcontrols, &tmprect);
  1025.     }
  1026.     if (map->moveonclick && map->autoselect) {
  1027.         SetRect(&tmprect, 4, 5, 26, 26);
  1028.         InvertRect(&tmprect);
  1029.     }
  1030.     /* (should modify appearance of top left arrow buttons to reflect abilities) */
  1031.     basev = 32 + 5*15 + 2 + 5/*why?*/ + 1;
  1032.     SetRect(&tmprect, 0, basev, 30, basev + 10);
  1033.     if (map->drawgrid) {
  1034.         InvertRect(&tmprect);
  1035.     }
  1036.     OffsetRect(&tmprect, 0, 11);
  1037.     if (map->drawnames) {
  1038.         InvertRect(&tmprect);
  1039.     }
  1040.     OffsetRect(&tmprect, 0, 11);
  1041.     if (map->drawpeople) {
  1042.         InvertRect(&tmprect);
  1043.     } else if (!people_sides_defined()) {
  1044.         gray_out_rect(&tmprect);
  1045.     }
  1046.     OffsetRect(&tmprect, 0, 11);
  1047.     if (map->drawplans) {
  1048.         InvertRect(&tmprect);
  1049.     }
  1050.     OffsetRect(&tmprect, 0, 11);
  1051.     if (map->drawai) {
  1052.         InvertRect(&tmprect);
  1053.     } else if (!side_has_ai(dside)) {
  1054.         /* (should ensure that this is updated when side gets an AI) */
  1055.         gray_out_rect(&tmprect);
  1056.     }
  1057.     /* Draw state of bottom left control buttons. */
  1058.     if (map->power <= 0) {
  1059.         SetRect(&tmprect, 0, winhgt - 15, 15, winhgt);
  1060.         gray_out_rect(&tmprect);
  1061.     }
  1062.     if (map->power >= NUMPOWERS - 1) {
  1063.         SetRect(&tmprect, 16, winhgt - 15, 30, winhgt);
  1064.         gray_out_rect(&tmprect);
  1065.     }
  1066. }
  1067.  
  1068. draw_top_line(map)
  1069. Map *map;
  1070. {
  1071.     int numchars;
  1072.     Rect tmprect;
  1073.  
  1074.     /* Clear the whole topline area. */
  1075.     SetRect(&tmprect, conwid, 0, conwid + map->pxw, tophgt);
  1076.     FillRect(&tmprect, QD(white));
  1077.     /* Draw a line dividing this from the map content. */
  1078.     MoveTo(conwid, tmprect.bottom - 1);  Line(map->pxw, 0);
  1079.     /* Draw the current date. */
  1080.     TextSize(10);
  1081.     MoveTo(tmprect.right - 60, tmprect.top + 11);
  1082.     DrawString(curdatestr);
  1083.     if (mouseover != NULL) {
  1084.         /* Draw description of what the mouse is over. */
  1085.         /* (should clip to avail space) */
  1086.         numchars = strlen(mouseover);
  1087.         MoveTo(conwid + 10, tmprect.top + 11);
  1088.         DrawText(mouseover, 0, numchars);
  1089.     }
  1090. }
  1091.  
  1092. /* Draw an indication of the position of other maps relative to this one. */
  1093.  
  1094. draw_other_maps(map)
  1095. Map *map;
  1096. {
  1097.     Map *map2;
  1098.  
  1099.     for_all_maps(map2) {
  1100.         if (map != map2 /* && reasonable to show? */) {
  1101.             draw_other_map(map, map2);
  1102.         }
  1103.     }
  1104. }
  1105.  
  1106. draw_related_maps(map)
  1107. Map *map;
  1108. {
  1109.     Map *map2;
  1110.     GrafPtr oldport;
  1111.  
  1112.     for_all_maps(map2) {
  1113.         if (map != map2 && map2->drawothermaps /* && reasonable to show? */) {
  1114.             GetPort(&oldport);
  1115.             SetPort(map2->window);
  1116.             /* (also clipping?) */
  1117.             draw_other_map(map2, map);
  1118.             SetPort(oldport);
  1119.         }
  1120.     }
  1121. }
  1122.  
  1123. draw_other_map(map, map2)
  1124. Map *map, *map2;
  1125. {
  1126.     int sx, sy, sw, sh;
  1127.     Rect tmprect;
  1128.  
  1129.     sx = (map2->sx * map->hw) / map2->hw - map->sx;
  1130.     sy = (map2->sy * map->hch) / map2->hch - map->sy;
  1131.     sw = (map2->pxw * map->hw) / map2->hw;
  1132.     sh = (map2->pxh * map->hch) / map2->hch;
  1133.     SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
  1134.     OffsetRect(&tmprect, conwid, map->toph);
  1135.     if (map->hw < 8) PenSize(2, 2);
  1136.     PenMode(patXor);
  1137.     FrameRect(&tmprect);
  1138.     PenNormal();
  1139. }
  1140.  
  1141. /* x0 may be negative here. */
  1142.  
  1143. draw_row(map, x0, y0, len, clearit)
  1144. Map *map;
  1145. int x0, y0, len, clearit;
  1146. {
  1147.     int empty = FALSE;
  1148.     char ch;
  1149.     int i = 0, x, sx, sy, t;
  1150.  
  1151.     if (!between(0, y0, area.height - 1)) return;
  1152.  
  1153.     if (partial_views() && !map->drawai) {
  1154.         empty = TRUE;
  1155.         /* Examine row to see if we can skip it entirely. */
  1156.         for (x = x0; x < x0 + len; ++x) {
  1157.             if (terrain_visible(wrapx(x), y0)) { empty = FALSE;  break; }
  1158.             /* (only need following test if border types possible) */
  1159.             if (numbordtypes > 0 && terrain_visible(wrapx(x), y0 + 1)) { empty = FALSE;  break; }
  1160.         }
  1161.     }
  1162.     if (empty) return;
  1163.     /* The terrain always comes first. */
  1164.     if (map->drawterrain) {
  1165.         draw_terrain_row(map, x0, y0, len);
  1166.     }
  1167.     if (map->drawterrain && any_aux_terrain_defined()) {
  1168.         /* The relative ordering of these is quite important - connections
  1169.            should always be drawn on top of borders.  Note that
  1170.            each should be prepared to run independently also, since the
  1171.            other displays might have been turned off. (?) */
  1172.         if (bords_to_draw(map)) {
  1173.             for_all_terrain_types(t) {
  1174.                 if (t_is_border(t) && aux_terrain_defined(t)) {
  1175.                     for (x = x0; x < x0 + len; ++x) {
  1176.                         draw_borders(map, x, y0, t);
  1177.                     }
  1178.                 }
  1179.             }
  1180.         }
  1181.         if (conns_to_draw(map)) {
  1182.             for_all_terrain_types(t) {
  1183.                 if (t_is_connection(t) && aux_terrain_defined(t)) {
  1184.                     for (x = x0; x < x0 + len; ++x) {
  1185.                         draw_connections(map, x, y0, t);
  1186.                     }
  1187.                 }
  1188.             }
  1189.         }
  1190.     }
  1191.     /* Although we had to draw the terrain on the edge, we can skip everything else. */
  1192.     /* Skip the top and bottom edge rows. */
  1193.     if (!between(1, y0, area.height - 2)) return;
  1194.     /* Shorten the row. */
  1195.     if (!inside_area(x0+len-1, y0)) --len;
  1196.     if (len <= 0) return;
  1197.     if (!inside_area(x0, y0)) ++x0;
  1198.     if (any_cell_materials_defined() && map->nummaterialstodraw > 0 && map->hh > 20) {
  1199.         for (x = x0; x < x0 + len; ++x) {
  1200.             draw_materials(map, x, y0);
  1201.         }
  1202.     }
  1203.     /* (should be global to entire map drawing somehow?) */
  1204.     if (map->drawnames && map->hh > 5) {
  1205.         for (x = x0; x < x0 + len; ++x) {
  1206.             if (!inside_area(x, y0)) continue;
  1207.             draw_legend(map, x, y0);
  1208.         }
  1209.     }
  1210.     if (people_sides_defined() && bwid2[map->power] > 0) {
  1211.         draw_people_row(map, x0, y0, len);
  1212.     }
  1213.     if (map->drawunits && map->hw > 2) {
  1214.         for (x = x0; x < x0 + len; ++x) {
  1215.             draw_units(map, x, y0);
  1216.         }
  1217.     }
  1218.     if (map->drawai) {
  1219.         for (x = x0; x < x0 + len; ++x) {
  1220.             draw_theater(map, x, y0);
  1221.         }
  1222.     }
  1223.     /* If debugging, draw coverage on top of everything else. */
  1224.     if (DebugG && !g_see_all()) {
  1225.         for (x = x0; x < x0 + len; ++x) {
  1226.             xform(map, x, y0, &sx, &sy);
  1227.             draw_coverage(sx, sy, map->power, cover(dside, x, y0));
  1228.         }
  1229.     }
  1230. }
  1231.  
  1232. hex_update(map, x, y)
  1233. Map *map;
  1234. int x, y;
  1235. {
  1236.     int sx, sy;
  1237.     Rect tmprect;
  1238.  
  1239.     xform(map, x, y, &sx, &sy);
  1240.     SetRect(&tmprect, sx, sy, sx + map->hw, sy + map->hh);
  1241.     return (RectInRgn(&tmprect, map->window->visRgn));
  1242. }
  1243.  
  1244. /* Draw an entire row of terrain, possibly with a single rectangle fill. */
  1245.  
  1246. /* x0 may be negative. */
  1247.  
  1248. extern int sunx, suny;
  1249.  
  1250. draw_terrain_row(map, x0, y0, len)
  1251. Map *map;
  1252. int x0, y0, len;
  1253. {
  1254.     int empty;
  1255.     int x, x1, t, sx, sy, w, h, p = map->power;
  1256.     int dogrid = map->drawgrid, dofill = map->drawhexpats;
  1257.     int i = 0;
  1258.     int inarea, seginarea;
  1259.     int style, segstyle;
  1260.     int terr, segterr;
  1261.     int over, segover;
  1262.     int update, segupdate;
  1263.     Rect tmprect, rect;
  1264.     Image *timg;
  1265.  
  1266.     tmpdrawlighting = map->drawlighting;
  1267.     x1 = x0;
  1268.     seginarea = in_area(x0, y0);
  1269.     segstyle = hex_style(x0, y0, p);
  1270.     segterr = hex_terrain(x0, y0, p);
  1271.     segover = hex_overlay(x0, y0);
  1272.     segupdate = (seginarea ? TRUE /* hex_update(map, x0, y0) */ : FALSE);
  1273.     for (x = x0; x < x0 + len + 1; ++x) {
  1274.         inarea = in_area(x, y0);
  1275.         style = hex_style(x, y0, p);
  1276.         terr = hex_terrain(x, y0, p);
  1277.         over = hex_overlay(x, y0);
  1278.         update = (inarea ? TRUE /* hex_update(map, x, y0) */ : FALSE);
  1279.         /* Decide if the run is over and we need to dump some output. */
  1280.         if (x == x0 + len
  1281.             || inarea != seginarea
  1282.             || style != segstyle
  1283.             || terr != segterr
  1284.             || over != segover
  1285.             || update != segupdate
  1286.             || segstyle == usepictures
  1287.             || segstyle == usepolygons) {
  1288.             /* don't draw anything that would match the window's bg */
  1289.             if (seginarea && segupdate && segstyle != dontdraw) {
  1290.                 xform(map, x1, y0, &sx, &sy);
  1291.                 switch (segstyle) {
  1292.                     case useblocks:
  1293.                         draw_hex_block(sx, sy, i, map->power, segterr, segover);
  1294.                         break;
  1295.                     case usepictures:
  1296.                         break;
  1297.                     case usepolygons:
  1298.                         draw_hex_region(sx, sy, map->power, map->drawgrid, segterr, segover);
  1299.                         /* Assume that only polygon scale can fit numbers. */
  1300.                         if (elevations_defined()
  1301.                             && map->drawelevations
  1302.                             && 1 /* draw_elevation_here(x1, y0) */) {
  1303.                             draw_elevation(sx, sy, map->power, elev_at(x1, y0));
  1304.                         }
  1305.                         if (clouds_defined()
  1306.                             && map->drawclouds
  1307.                             && draw_clouds_here(x1, y0)) {
  1308.                             draw_clouds(sx, sy, map->power, raw_cloud_at(x1, y0), 0, 0);
  1309.                         }
  1310.                         if (winds_defined()
  1311.                             && map->drawwinds
  1312.                             && draw_winds_here(x1, y0)) {
  1313.                             draw_winds(sx, sy, map->power, wind_dir_at(x1, y0), wind_force_at(x1, y0));
  1314.                         }
  1315.                         if (temperatures_defined()
  1316.                             && map->drawtemperature
  1317.                             && draw_temperature_here(x1, y0)) {
  1318.                             draw_temperature(sx, sy, map->power, temperature_at(x1, y0));
  1319.                         }
  1320.                 }
  1321.             }
  1322.             i = 0;
  1323.             x1 = x;
  1324.             seginarea = inarea;
  1325.             segstyle = style;
  1326.             segterr = terr;
  1327.             segover = over;
  1328.             segupdate = update;
  1329.         }
  1330.         ++i;
  1331.     }
  1332. }
  1333.  
  1334. draw_borders(map, x, y, b)
  1335. Map *map;
  1336. int x, y, b;
  1337. {
  1338.     int dir, sx, sy, bitmask = 0;
  1339.     
  1340.      if (!terrain_visible(wrapx(x), y) || !any_borders_at(x, y, b)) return;
  1341.     for_all_directions(dir) {
  1342.         if (border_at(x, y, dir, b) && borders_visible(x, y, dir)) {
  1343.             bitmask |= 1 << dir;
  1344.         }
  1345.     }
  1346.     if (bitmask != 0) {
  1347.         xform(map, x, y, &sx, &sy);
  1348.         draw_border_line_multiple(map->window, sx, sy, bitmask, map->power, b);
  1349.     }
  1350. }
  1351.  
  1352. /* Draw all the connections of the given hex. */
  1353.  
  1354. draw_connections(map, x, y, c)
  1355. Map *map;
  1356. int x, y, c;
  1357. {
  1358.     int sx, sy, dir, bitmask = 0, t = terrain_at(x, y);
  1359.     
  1360.     if (!terrain_visible(wrapx(x), y) || !any_connections_at(x, y, c)) return;
  1361.     for_all_directions(dir) {
  1362.         if (connection_at(x, y, dir, c)) {
  1363.             bitmask |= 1 << dir;
  1364.         }
  1365.     }
  1366.     if (bitmask != 0) {
  1367.         xform(map, x, y, &sx, &sy);
  1368.         draw_connection_line_multiple(map->window, sx, sy, bitmask, map->power, c);
  1369.     }
  1370. }
  1371.  
  1372. /* Draw all the units visible in the given cell. */
  1373.  
  1374. draw_units(map, x, y)
  1375. Map *map;
  1376. int x, y;
  1377. {
  1378.     short uview;
  1379.     int sx, sy, sw, sh, u, s;
  1380.     int terr = terrain_at(wrapx(x), y);
  1381.     Side *side2;
  1382.     Unit *unit;
  1383.     Rect tmprect;
  1384.     extern PicHandle dotdotdotpicture;
  1385.  
  1386.     if (units_visible(x, y) && unit_at(x, y) != NULL) {
  1387.         xform(map, x, y, &sx, &sy);
  1388.         if (map->uw <= 16) {
  1389.             unit = unit_at(x, y);
  1390.             /* Adjust to unit part of cell. */
  1391.             sx += (map->hw - map->uw) / 2;  sy += (map->hh - map->uh) / 2;
  1392.             draw_unit_image(map->window, sx, sy, map->uw, map->uh,
  1393.                             unit->type, side_number(unit->side), !completed(unit));
  1394.             /* Indicate if more than one stacked here. */
  1395.             if (unit->nexthere != NULL && map->uw > 8) {
  1396.                 SetRect(&tmprect, sx + map->uw/2 - 6, sy + map->uh - 2,
  1397.                                   sx + map->uw/2 + 6, sy + map->uh + 2);
  1398.                 /* (should clip to fit in hex) */
  1399.                 DrawPicture(dotdotdotpicture, &tmprect);
  1400.             }
  1401.             if (map->drawnames) draw_unit_name(unit, sx, sy, map->uw, map->uh);
  1402.         } else {
  1403.             for_all_stack(x, y, unit) {
  1404.                 xform_unit(map, unit, &sx, &sy, &sw, &sh);
  1405.                 draw_unit_and_occs(map, unit, sx, sy, sw, sh);
  1406.             }
  1407.         }
  1408.     } else {
  1409.         if ((uview = unit_view(dside, x, y)) != EMPTY) {
  1410.             u = vtype(uview);  s = vside(uview);
  1411.             xform(map, x, y, &sx, &sy);
  1412.             /* Adjust to unit part of cell. */
  1413.             sx += (map->hw - map->uw) / 2;  sy += (map->hh - map->uh) / 2;
  1414.             draw_unit_image(map->window, sx, sy, map->uw, map->uh, u, s, 0);
  1415.         }
  1416.     }
  1417. }
  1418.  
  1419. draw_unit_and_occs(map, unit, sx, sy, sw, sh)
  1420. Map *map;
  1421. Unit *unit;
  1422. int sx, sy, sw, sh;
  1423. {
  1424.     int u = unit->type, s = side_number(unit->side), sx2, sy2, sw2, sh2;
  1425.     Unit *occ;
  1426.     Rect tmprect;
  1427.  
  1428.     /* If an occupant's side is the same as its transport's, then there's
  1429.        really no need to draw its side emblem, since the transport's emblem
  1430.        will also be visible. */
  1431.     if (unit->transport && unit->side == unit->transport->side) s = -1;
  1432.     if (unit->occupant == NULL) {
  1433.         draw_unit_image(map->window, sx, sy, sw, sh, u, s, !completed(unit));
  1434.         if (map->drawnames) draw_unit_name(unit, sx, sy, sw, sh); 
  1435.     } else {
  1436.         /* Draw a sort of "grouping box", in white. */
  1437.         SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
  1438.         FillRect(&tmprect, QD(white));
  1439.         FrameRect(&tmprect);
  1440.         /* Draw the transport in the UL quarter of the box. */
  1441.         xform_occupant(map, unit, unit, sx, sy, sw, sh, &sx2, &sy2, &sw2, &sh2);
  1442.         draw_unit_image(map->window, sx2, sy2, sw2, sh2, u, s, !completed(unit));
  1443.         if (map->drawnames) draw_unit_name(unit, sx2, sy2, sw2, sh2);
  1444.         /* Draw all the occupants, in the bottom half of the box. */
  1445.         for_all_occupants(unit, occ) {
  1446.             xform_occupant(map, unit, occ, sx, sy, sw, sh, &sx2, &sy2, &sw2, &sh2);
  1447.             draw_unit_and_occs(map, occ, sx2, sy2, sw2, sh2);
  1448.         }
  1449.     }
  1450. }
  1451.  
  1452. /* Indicate what kind of people are living in the given hex. */
  1453.  
  1454. draw_people_row(map, x0, y, len)
  1455. Map *map;
  1456. int x0, y, len;
  1457. {
  1458.     int pop, xx, x, sx, sy, sw, sh, ex, ey, ew, eh, dir, x1, y1, pop1;
  1459.     int bitmask1, bitmask2, drawemblemhere;
  1460.  
  1461.     for (xx = x0; xx < x0 + len; ++xx) {
  1462.         x = wrapx(xx);
  1463.         if (!terrain_visible(x, y)) continue;
  1464.         pop = people_side_at(x, y);
  1465.         bitmask1 = bitmask2 = 0;
  1466.         drawemblemhere = FALSE;
  1467.         /* Decide which edges are borders of the country. */
  1468.         for_all_directions(dir) {
  1469.             /* Don't do anything about edge cells. */
  1470.             if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
  1471.                 if (terrain_visible(x1, y1)) {
  1472.                     pop1 = people_side_at(x1, y1);
  1473.                     if (pop != pop1) {
  1474.                         /* Borders with uninhabitated regions are drawn differently. */
  1475.                         if (pop == NOBODY || pop1 == NOBODY) {
  1476.                             bitmask2 |= 1 << dir;
  1477.                         } else {
  1478.                             bitmask1 |= 1 << dir;
  1479.                         }
  1480.                     }
  1481.                 } else {
  1482.                     /* Draw just people in the cells right at the edge of the known world. */
  1483.                     drawemblemhere = TRUE;
  1484.                 }
  1485.             }
  1486.         }
  1487.         /* Now draw both the edges and an emblem for the cell. */
  1488.         if ((bitmask1 | bitmask2) != 0 || (map->drawpeople && drawemblemhere)) {
  1489.             xform(map, x, y, &sx, &sy);
  1490.             if (bitmask1 != 0) {
  1491.                 draw_country_borders(map->window, sx, sy, bitmask1, map->power, 0);
  1492.             }
  1493.             if (bitmask2 != 0) {
  1494.                 draw_country_borders(map->window, sx, sy, bitmask2, map->power, 2);
  1495.             }
  1496.             /* Draw an emblem for the people in the cell. */
  1497.             if (map->drawpeople && pop != NOBODY) {
  1498.                 sw = map->uw;  sh = map->uh;
  1499.                 ew = min(sw, max(8, sw / 2));  eh = min(sh, max(8, sh / 2));
  1500.                 ex = sx + (map->hw - map->uw) / 2 + sw / 2 - ew / 2;  ey = sy + (map->hh - map->uh) / 2 + sh / 2 - eh / 2;
  1501.                 draw_side_emblem(map->window, ex, ey, ew, eh, pop);
  1502.             }
  1503.         }
  1504.     }
  1505. }
  1506.  
  1507. #if 0
  1508. draw_people(map, x, y)
  1509. Map *map;
  1510. int x, y;
  1511. {
  1512.     int pop, sx, sy, sw, sh, ex, ey, ew, eh, dir, x1, y1, pop1;
  1513.     int bitmask1 = 0, bitmask2 = 0, drawemblemhere = FALSE;
  1514.  
  1515.     if (!terrain_visible(x, y)) return;
  1516.     pop = people_side_at(wrapx(x), y);
  1517.     /* Decide which edges are borders of the country. */
  1518.     for_all_directions(dir) {
  1519.         /* Don't do anything about edge cells. */
  1520.         if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
  1521.             if (terrain_visible(x1, y1)) {
  1522.                 pop1 = people_side_at(x1, y1);
  1523.                 if (pop != pop1) {
  1524.                     /* Borders with uninhabitated regions are drawn differently. */
  1525.                     if (pop == NOBODY || pop1 == NOBODY) {
  1526.                         bitmask2 |= 1 << dir;
  1527.                     } else {
  1528.                         bitmask1 |= 1 << dir;
  1529.                     }
  1530.                 }
  1531.             } else {
  1532.                 /* Draw just people in the cells right at the edge of the known world. */
  1533.                 drawemblemhere = TRUE;
  1534.             }
  1535.         }
  1536.     }
  1537.     /* Now draw both the edges and an emblem for the cell. */
  1538.     if ((bitmask1 | bitmask2) != 0 || (map->drawpeople && drawemblemhere)) {
  1539.         xform(map, x, y, &sx, &sy);
  1540.         if (bitmask1 != 0) {
  1541.             draw_country_borders(map->window, sx, sy, bitmask1, map->power, 0);
  1542.         }
  1543.         if (bitmask2 != 0) {
  1544.             draw_country_borders(map->window, sx, sy, bitmask2, map->power, 2);
  1545.         }
  1546.         /* Draw an emblem for the people in the cell. */
  1547.         if (map->drawpeople && pop != NOBODY) {
  1548.             sw = map->uw;  sh = map->uh;
  1549.             ew = min(sw, max(8, sw / 2));  eh = min(sh, max(8, sh / 2));
  1550.             ex = sx + (map->hw - map->uw) / 2 + sw / 2 - ew / 2;  ey = sy + (map->hh - map->uh) / 2 + sh / 2 - eh / 2;
  1551.             draw_side_emblem(map->window, ex, ey, ew, eh, pop);
  1552.         }
  1553.     }
  1554. }
  1555. #endif
  1556.  
  1557. /* This draws a small set of bar charts, one for each material type. */
  1558.  
  1559. draw_materials(map, x, y)
  1560. Map *map;
  1561. int x, y;
  1562. {
  1563.     int m, t, sx, sy, mx, my, mw, mh, amt, maxamt, h;
  1564.     Rect graphrect;
  1565.     
  1566.     if (nummtypes == 0) return;
  1567.     mw = map->uw / nummtypes /* should be count of displayable materials... */;  mh = map->uh;
  1568.     if (mw <= 2 || mh <= 2) return;
  1569.     t = terrain_at(x, y);
  1570.     xform(map, x, y, &sx, &sy);
  1571.     mx = sx + (map->hw - map->uw) / 2;  my = sy + (map->hh - map->uh) / 2;
  1572.     for_all_material_types(m) {
  1573.         if (map->drawmaterials[m] && (maxamt = tm_storage_x(t, m)) > 0) {
  1574.             SetRect(&graphrect, mx + m * mw, my, mx + (m + 1) * mw, my + map->uh);
  1575.             FrameRect(&graphrect);
  1576.             amt = material_at(x, y, m);
  1577.             h = (amt * mh) / maxamt;
  1578.             graphrect.top -= (mh - h);
  1579.             FillRect(&graphrect, QD(black));
  1580.         }
  1581.     }
  1582. }
  1583.  
  1584. draw_theater(map, x, y)
  1585. Map *map;
  1586. int x, y;
  1587. {
  1588.     int thid, sx, sy, sw, sh, ex, ey, ew, eh, dir, x1, y1, thid1;
  1589.     int bitmask = 0;
  1590.  
  1591.     thid = ai_theater_at(dside, wrapx(x), y);
  1592.     /* Decide which edges are borders of the theater. */
  1593.     for_all_directions(dir) {
  1594.         /* Don't do anything about edge cells. */
  1595.         if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
  1596.             thid1 = ai_theater_at(dside, x1, y1);
  1597.             if (thid != thid1) {
  1598.                 bitmask |= 1 << dir;
  1599.             }
  1600.         }
  1601.     }
  1602.     if (bitmask != 0) {
  1603.         xform(map, x, y, &sx, &sy);
  1604.         if (bitmask != 0) {
  1605.             draw_theater_borders(map->window, sx, sy, bitmask, map->power);
  1606.         }
  1607.     }
  1608. }
  1609.  
  1610. /* Draw any text that should be associated with this hex. */
  1611.  
  1612. /* (could precompute what the string will lap over and move or truncate str),
  1613.    should be deterministic for each mag, so redraw doesn't scramble */
  1614.  
  1615. char *feature_desc();
  1616.  
  1617. draw_legend(map, x, y)
  1618. Map *map;
  1619. int x, y;
  1620. {
  1621.     int xx, numhexes = 1, color, sx, sy, pixlen;
  1622.     char *str, buf[BUFSIZE];
  1623.     Unit *unit;
  1624.     Feature *feature;
  1625.  
  1626.     /* Draw the name of a terrain feature. */
  1627.     /* (should limit to one cell of feature, preferably centering on quasi-centroid) */    
  1628.     if (terrain_visible(wrapx(x), y)) {
  1629.         if ((feature = feature_at(x, y)) != NULL) {
  1630.             if (1 /* feature->size > 0  make sure this is calced! */) {
  1631.                 if (1 /* center of feature, or center far away */) {
  1632.                     if ((str = feature_desc(feature, buf)) != NULL) {
  1633.                         xform(map, x, y, &sx, &sy);
  1634.                         draw_legend_text(sx + map->hw/2, sy + map->hh/2, map->uh, str, 0);
  1635.                     }
  1636.                 }
  1637.             }
  1638.         }
  1639.     }
  1640. }
  1641.  
  1642. draw_blast(unit, side3, hit)
  1643. Unit *unit;
  1644. Side *side3;
  1645. int hit;
  1646. {
  1647.     int ux = unit->x, uy = unit->y, sx, sy, sw, sh, i, btype;
  1648.     int startticks, innerticks;
  1649.     Side *us = unit->side, *side;
  1650.     Map *map;
  1651.     GrafPtr oldport, curport = NULL;
  1652.  
  1653.     btype = ((hit >= unit->hp) ? 2 : ((hit > 0) ? 1 : 0));
  1654.     if (active_display(side3) && units_visible(ux, uy)) {
  1655.         GetPort(&oldport);
  1656.         startticks = TickCount();
  1657.         i = 0;
  1658.         /* Tweak the pen modes of all the maps. */
  1659.         for_all_maps(map) {
  1660.             SetPort(map->window);
  1661.             PenMode(patXor);
  1662.             if (map->hw > 10) PenSize(2, 2);
  1663.             else PenSize(1, 1);
  1664.         }
  1665.         while (TickCount() < startticks + 32) {
  1666.             innerticks = TickCount();
  1667.             for_all_maps(map) {
  1668.                 if (curport != map->window) {
  1669.                     SetPort(map->window);
  1670.                     curport = map->window;
  1671.                 }
  1672.                 xform_unit(map, unit, &sx, &sy, &sw, &sh);
  1673.                 draw_blast_image(map->window, sx, sy, sw, sh, btype);
  1674.             }
  1675.             while (TickCount() < innerticks + 32);
  1676.             ++i;
  1677.         }
  1678.         /* Restore the pen modes of all the maps. */
  1679.         for_all_maps(map) {
  1680.             SetPort(map->window);
  1681.             PenNormal();
  1682.         }
  1683.         SetPort(oldport);
  1684.     }
  1685. }
  1686.  
  1687. /* Draw all the selections of all the units. */
  1688.  
  1689. draw_selections(map)
  1690. Map *map;
  1691. {
  1692.     int sx, sy, sx2, sy2, i;
  1693.     GrafPtr oldport;
  1694.     Rect tmprect;
  1695.     RgnHandle tmprgn;
  1696.     Unit *unit;
  1697.  
  1698.      GetPort(&oldport);
  1699.     SetPort(map->window);
  1700.     tmprgn = NewRgn();
  1701.     GetClip(tmprgn);
  1702.     ClipRect(&(map->contentrect));
  1703.     for (i = 0; i < map->numselections; ++i) {
  1704.         unit = map->selections[i];
  1705.         draw_selected_unit(map, unit);
  1706.     }
  1707.     SetClip(tmprgn);
  1708.     DisposeRgn(tmprgn);
  1709.     SetPort(oldport);
  1710. }
  1711.  
  1712. /* Draw all the selected units in the given cell. */
  1713.  
  1714. draw_selections_at(map, x, y)
  1715. Map *map;
  1716. int x, y;
  1717. {
  1718.     int i;
  1719.     GrafPtr oldport;
  1720.     RgnHandle tmprgn;
  1721.     Unit *unit;
  1722.  
  1723.      GetPort(&oldport);
  1724.     SetPort(map->window);
  1725.     tmprgn = NewRgn();
  1726.     GetClip(tmprgn);
  1727.     ClipRect(&(map->contentrect));
  1728.     for (i = 0; i < map->numselections; ++i) {
  1729.         unit = map->selections[i];
  1730.         if (unit && unit->x == x && unit->y == y) {
  1731.             draw_selected_unit(map, unit);
  1732.         }
  1733.     }
  1734.     SetClip(tmprgn);
  1735.     DisposeRgn(tmprgn);
  1736.     SetPort(oldport);
  1737. }
  1738.  
  1739. draw_selected_unit_setport(map, unit)
  1740. Map *map;
  1741. Unit *unit;
  1742. {
  1743.     int i;
  1744.     GrafPtr oldport;
  1745.     RgnHandle tmprgn;
  1746.  
  1747.      GetPort(&oldport);
  1748.     SetPort(map->window);
  1749.     tmprgn = NewRgn();
  1750.     GetClip(tmprgn);
  1751.     ClipRect(&(map->contentrect));
  1752.     draw_selected_unit(map, unit);
  1753.     SetClip(tmprgn);
  1754.     DisposeRgn(tmprgn);
  1755.     SetPort(oldport);
  1756. }
  1757.  
  1758. /* Draw a single selected unit on the given map.  Assumes that grafport already set. */
  1759.  
  1760. draw_selected_unit(map, unit)
  1761. Map *map;
  1762. Unit *unit;
  1763. {
  1764.     int sx, sy, sw, sh, size, wholecell = FALSE, drawmag = FALSE;
  1765.     int sx1, sy1, sw1, sh1;
  1766.     Rect tmprect;
  1767.  
  1768.     if (!in_play(unit)) return; /* unselect it too? */
  1769.     if (map->uw >= 32) {
  1770.         xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
  1771.         if (map->numselections == 1
  1772.             && sw < 16
  1773.             && unit->transport != NULL) {
  1774.             wholecell = TRUE;
  1775.             drawmag = TRUE;
  1776.             sx1 = sx;  sy1 = sy;  sw1 = sw;  sh1 = sh;
  1777.         }
  1778.     } else {
  1779.         wholecell = TRUE;
  1780.     }
  1781.     if (wholecell) {
  1782.         xform(map, unit->x, unit->y, &sx, &sy);
  1783.         /* Adjust to unit part of cell. */
  1784.         sx += (map->hw - map->uw) / 2;  sy += (map->hh - map->uh) / 2;
  1785.         sw = map->uw;  sh = map->uh;
  1786.     }
  1787.     if (0 /* not actually within visible area */) return;
  1788.     if (map->drawplans
  1789.         && unit->plan
  1790.         && unit->plan->tasks) {
  1791.         int sx2, sy2;
  1792.         Task *task = unit->plan->tasks, *nexttask;
  1793.  
  1794.         if (task != NULL) {
  1795.             if ((nexttask = task->next) != NULL) {
  1796.                 switch (nexttask->type) {
  1797.                     case MOVETO_TASK:
  1798.                     case HIT_UNIT_TASK:
  1799.                         xform(map, nexttask->args[0], nexttask->args[1], &sx2, &sy2);
  1800.                         PenPat(QD(dkGray));
  1801.                         MoveTo(sx + sw/2, sy + sh/2);
  1802.                         LineTo(sx2 + map->hw/2, sy2 + map->hh/2);
  1803.                         PenNormal();
  1804.                 }
  1805.             }
  1806.             switch (task->type) {
  1807.                 case MOVETO_TASK:
  1808.                 case HIT_UNIT_TASK:
  1809.                     xform(map, task->args[0], task->args[1], &sx2, &sy2);
  1810.                     PenPat(QD(dkGray));
  1811.                     MoveTo(sx + sw/2, sy + sh/2);
  1812.                     LineTo(sx2 + map->hw/2, sy2 + map->hh/2);
  1813.                     PenNormal();
  1814.             }
  1815.         }
  1816.     }
  1817.     /* Draw magnification lines pointing to the true location of the unit. */
  1818.     if (drawmag) {
  1819.         /* PenPat should already be black. */
  1820.         MoveTo(sx,      sy);       LineTo(sx1,       sy1);
  1821.         MoveTo(sx + sw, sy);       LineTo(sx1 + sw1, sy1);
  1822.         MoveTo(sx,      sy + sh);  LineTo(sx1,       sy1 + sh1);
  1823.         MoveTo(sx + sw, sy + sh);  LineTo(sx1 + sw1, sy1 + sh1);
  1824.     }
  1825.     /* Be sure the selected unit is drawn. */
  1826.     draw_unit_image(map->window, sx, sy, sw, sh,
  1827.                     unit->type, side_number(unit->side), !completed(unit));
  1828.     /* Draw a highlighting rectangle. */
  1829.     SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
  1830.     /* A hack to prevent leakage into the grid. */
  1831.     if (map->drawgrid && map->power == 5) --tmprect.bottom;
  1832.     /* First, draw an outer white frame, for contrast. */
  1833.     PenPat(QD(white));
  1834.     FrameRect(&tmprect);
  1835.     InsetRect(&tmprect, 1, 1);
  1836.     /* Black is for units that can still act, dark gray for actors, gray if the
  1837.        unit can't do anything. */
  1838.     PenPat((unit->act && unit->act->initacp > 0) ?
  1839.             ((unit->act->acp > 0) ? QD(black) : QD(dkGray)) : QD(gray));
  1840.     /* Wide border if awake, narrow if asleep or napping. */
  1841.     size = ((unit->plan && (unit->plan->asleep || unit->plan->reserve)) ? 1 : 2);
  1842.     PenSize(size, size);
  1843.     FrameRect(&tmprect);
  1844.     PenNormal();
  1845.     DGprintf("draw selection of %s at %d,%d\n", unit_desig(unit));
  1846. }
  1847.  
  1848. /* (should only redraw any given cell once) */
  1849.  
  1850. erase_selections(map)
  1851. Map *map;
  1852. {
  1853.     int sx, sy, sx2, sy2, i;
  1854.     GrafPtr oldport;
  1855.     RgnHandle tmprgn;
  1856.     Unit *unit;
  1857.  
  1858.      GetPort(&oldport);
  1859.     SetPort(map->window);
  1860.     tmprgn = NewRgn();
  1861.     GetClip(tmprgn);
  1862.     ClipRect(&(map->contentrect));
  1863.     for (i = 0; i < map->numselections; ++i) {
  1864.         unit = map->selections[i];
  1865.         draw_unselected_unit(map, unit);
  1866.     }
  1867.     SetClip(tmprgn);
  1868.     DisposeRgn(tmprgn);
  1869.     SetPort(oldport);
  1870. }
  1871.  
  1872. erase_selection(map, unit)
  1873. Map *map;
  1874. Unit *unit;
  1875. {
  1876.     GrafPtr oldport;
  1877.     RgnHandle tmprgn;
  1878.  
  1879.      GetPort(&oldport);
  1880.     SetPort(map->window);
  1881.     tmprgn = NewRgn();
  1882.     GetClip(tmprgn);
  1883.     ClipRect(&(map->contentrect));
  1884.     draw_unselected_unit(map, unit);
  1885.     SetClip(tmprgn);
  1886.     DisposeRgn(tmprgn);
  1887.     SetPort(oldport);
  1888. }
  1889.  
  1890. draw_unselected_unit(map, unit)
  1891. Map *map;
  1892. Unit *unit;
  1893. {
  1894.     if (!in_play(unit)) return;
  1895.     draw_row(map, unit->x, unit->y, 1, TRUE);
  1896.     DGprintf("erase selection of %s at %d,%d\n", unit_desig(unit));
  1897. }
  1898.  
  1899. force_map_update(map)
  1900. Map *map;
  1901. {
  1902.     force_update(map->window);
  1903. }
  1904.  
  1905. /* Remove and destroy the map object. */
  1906.  
  1907. destroy_map(map)
  1908. Map *map;
  1909. {
  1910.     Map *map2;
  1911.     
  1912.     if (maplist == map) {
  1913.         maplist = map->next;
  1914.     } else {
  1915.         for_all_maps(map2) {
  1916.             if (map2->next == map) {
  1917.                 map2->next = map->next;
  1918.             }
  1919.         }
  1920.     }
  1921.     /* (should destroy substructs) */
  1922.     free(map);
  1923. }
  1924.  
  1925. /* (should be able to mention borders and conns also - share code with curses?) */
  1926.  
  1927. oneliner(map, sx, sy)
  1928. Map *map;
  1929. int sx, sy;
  1930. {
  1931.     int x, y, t2, uview, u, s, ps = NOBODY, dep, sayin = FALSE;
  1932.     char *peopdesc = NULL, *sidedesc, *namedesc;
  1933.     char descbuf[80];
  1934.     Unit *unit;
  1935.     Side *side;
  1936.     char *mplayer_at_desig();
  1937.  
  1938.     if (!nearest_cell(map, sx, sy, &x, &y)) {
  1939.         strcpy(tmpbuf, "(nothing)");
  1940.         return;
  1941.     } else if (terrain_visible(x, y)) {
  1942.         strcpy(tmpbuf, " ");
  1943.         /* Describe the side of the people here. */
  1944.         if (people_sides_defined()) {
  1945.             ps = people_side_at(x, y);
  1946.             if (ps != NOBODY) {
  1947.                 side = side_n(ps);
  1948.                 if (side == NULL) {
  1949.                     peopdesc = "indep";
  1950.                 } else if (side == dside) {
  1951.                     peopdesc = "your";
  1952.                 } else {
  1953.                     peopdesc = side_adjective(side);
  1954.                     if (peopdesc[0] == '\0') {
  1955.                         sprintf(descbuf, "s%d", side->id);
  1956.                         peopdesc = descbuf;
  1957.                     }
  1958.                 }
  1959.             }
  1960.         }
  1961.         if (units_visible(x, y)) {
  1962.             nearest_unit(map, sx, sy, &unit);
  1963.             if (unit != NULL) {
  1964.                 if (unit->side != dside) {
  1965.                     sidedesc = side_adjective(unit->side);
  1966.                     if (ps != NOBODY && ps == side_number(unit->side)) {
  1967.                         peopdesc = "own";
  1968.                     }
  1969.                 } else {
  1970.                     sidedesc = "your";
  1971.                 }
  1972.                 strcat(tmpbuf, sidedesc);
  1973.                 if (unit->name) {
  1974.                     strcat(tmpbuf, " ");
  1975.                     strcat(tmpbuf, u_type_name(unit->type));
  1976.                     strcat(tmpbuf, " ");
  1977.                     strcat(tmpbuf, unit->name);
  1978.                 } else if (unit->number > 0) {
  1979.                     tprintf(tmpbuf, " %d%s %s",
  1980.                             unit->number, ordinal_suffix(unit->number), u_type_name(unit->type));
  1981.                 } else {
  1982.                     strcat(tmpbuf, " ");
  1983.                     strcat(tmpbuf, u_type_name(unit->type));
  1984.                 }
  1985.                 if (Debug || DebugG || DebugM) {
  1986.                     tprintf(tmpbuf, " #%d", unit->id);
  1987.                 }
  1988.                 sayin = TRUE;
  1989.             }
  1990.         } else {
  1991.             if ((uview = unit_view(dside, x, y)) != EMPTY) {
  1992.                 u = vtype(uview);  s = vside(uview);
  1993.                 if (ps != NOBODY && ps == s) {
  1994.                     peopdesc = "own";
  1995.                 }
  1996.                 strcat(tmpbuf, side_adjective(side_n(s)));
  1997.                 strcat(tmpbuf, " ");
  1998.                 strcat(tmpbuf, u_type_name(u));
  1999.                 sayin = TRUE;
  2000.             }
  2001.         }
  2002.         if (sayin) {
  2003.             strcat(tmpbuf, " (in ");
  2004.         }
  2005.         if (peopdesc != NULL) {
  2006.             strcat(tmpbuf, peopdesc);
  2007.             strcat(tmpbuf, " ");
  2008.         }
  2009.         strcat(tmpbuf, t_type_name(terrain_at(x, y)));
  2010.         if (sayin) {
  2011.             strcat(tmpbuf, ")");
  2012.         }
  2013.         if (elevations_defined()) {
  2014.             tprintf(tmpbuf, " Elev %d", elev_at(x, y));
  2015.         }
  2016.         if (temperatures_defined()) {
  2017.             tprintf(tmpbuf, " T %d°", temperature_at(x, y));
  2018.         }
  2019.         if (numcoattypes > 0) {
  2020.             for_all_terrain_types(t2) {
  2021.                 if (t_is_coating(t2)
  2022.                     && aux_terrain_defined(t2)
  2023.                     && ((dep = aux_terrain_at(x, y, t2)) > 0)) {
  2024.                     tprintf(tmpbuf, " %s %d", t_type_name(t2), dep);
  2025.                 }
  2026.             }
  2027.         }
  2028.     } else {
  2029.         sprintf(tmpbuf, "(unknown)");
  2030.     }
  2031.     tprintf(tmpbuf, " @%d,%d", x, y);
  2032.     if (map->drawai && side_has_ai(dside)) {
  2033.         strcat(tmpbuf, " ");
  2034.         strcat(tmpbuf, mplayer_at_desig(dside, x, y));
  2035.     }
  2036. }
  2037.  
  2038. activate_map(map, activate)
  2039. Map *map;
  2040. int activate;
  2041. {
  2042.     Rect growRect;
  2043.  
  2044.     if (activate) {
  2045.         HiliteControl(map->vscrollbar, 0);
  2046.         HiliteControl(map->hscrollbar, 0);
  2047. #if 0
  2048.         /* the controls must be redrawn on activation: */
  2049.         (*(map->vscrollbar))->contrlVis = 255;
  2050.         (*(map->hscrollbar))->contrlVis = 255;
  2051.         InvalRect(&(*(map->vscrollbar))->contrlRect);
  2052.         InvalRect(&(*(map->hscrollbar))->contrlRect);
  2053. #endif
  2054.         /* The growbox needs to be redrawn on activation. */
  2055.         growRect = map->window->portRect;
  2056.         /* adjust for the scrollbars */
  2057.         growRect.top = growRect.bottom - sbarwid;
  2058.         growRect.left = growRect.right - sbarwid;
  2059.         InvalRect(&growRect);
  2060.     } else {
  2061.         /* The scrollbars must be hidden on deactivation. */
  2062.         HiliteControl(map->vscrollbar, 255);
  2063.         HiliteControl(map->hscrollbar, 255);
  2064. /*        HideControl(map->vscrollbar);
  2065.         HideControl(map->hscrollbar); */
  2066.         /* The growbox should be changed immediately on deactivation. */
  2067.         DrawGrowIcon(map->window);
  2068.     }
  2069. }
  2070.  
  2071. print_map(map)
  2072. Map *map;
  2073. {
  2074. /*    TPPrPort printport;
  2075.     extern THPrint printrecordhandle;
  2076.  
  2077.     printport = PrOpenDoc(printrecordhandle, nil, nil);
  2078.     PrCloseDoc(printport); */
  2079. }
  2080.